import { isArray, isObject, isString } from '../../is';
import { Cookie, Local } from '../../storage';
import axios, { AxiosError, type AxiosResponse, type AxiosRequestConfig } from 'axios';
import { debounce } from '../../util';
import { ElNotification, ElMessage, ElMessageBox } from 'element-plus';
import JSONBIGINT from 'json-bigint';

export type ErrorMessageMode =
  | 'none'
  | 'modal'
  | 'message'
  | 'confirm'
  | undefined;
declare type Recordable<T = any> = Record<string, T>;

export class RequestOptions {
  /**
   * 超时时间
   */
  Timeout?: number = 1000 * 20;
  /**
   * 错误消息展示模式
   */
  ErrorMessageMode?: ErrorMessageMode = 'modal';

  /**
   * 返回值对象转换成时间
   */
  FormatResponseDate?: boolean = true;
  /**
 * 忽略授权登录
 */
  IgnoreAuth?: boolean = false;
}

/**
 * @description: Request result set
 */
export enum ResultEnum {
  SUCCESS = 0,
  ERROR = 1,
  TIMEOUT = 401,
  TYPE = 'success',
}

// multipart/form-data: upload file
export interface UploadFileParams {
  // Other parameters
  data?: any;
  // File parameter interface field name
  name?: string;
  // file name
  file: File | Blob;
  // file name
  filename?: string;

  [key: string]: any;

  //头部
  headers?: any;
}

/**
 * @description:  contentType
 */
export enum ContentTypeEnum {
  // json
  JSON = 'application/json;charset=UTF-8',
  // form-data qs
  FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8',
  // form-data  upload
  FORM_DATA = 'multipart/form-data;charset=UTF-8',
}

/**
 * 请求默认值
 */
const requestOptions: RequestOptions = {
  Timeout: 1000 * 20,
  ErrorMessageMode: 'message',
  FormatResponseDate: true,
};

const createAxios = (options: RequestOptions) => {
  // 创建一个 axios 实例
  const http = axios.create({
    baseURL: '/api', // 所有的请求地址前缀部分
    timeout: options.Timeout, // 请求超时时间毫秒
    withCredentials: true, // 异步请求携带cookie
    transformResponse: [
      (data) => {
        try {
          return JSONBIGINT.parse(data);
        } catch (err) {
          return data;
        }
      },
    ],
  });

  // 添加请求拦截器
  http.interceptors.request.use(
    function (config) {
      const userToken = Cookie.getCookie('Token');
      return config;
    },
    function (error) {
      // 对请求错误做些什么
      console.log(error);
      return Promise.reject(error);
    },
  );

  // 添加响应拦截器
  http.interceptors.response.use(
    function (response) {
      // 2xx 范围内的状态码都会触发该函数。
      // 对响应数据做点什么
      // dataAxios 是 axios 返回数据中的 data
      //         const dataAxios = response.data;
      // 这个状态码是和后端约定的
      //         const code = dataAxios.reset
      if (response.data) {
        if (options.FormatResponseDate) {
          //转换成时间
          formatResponseDate(response.data);
        }
      }
      return response;
    },
    function (error) {
      // 超出 2xx 范围的状态码都会触发该函数。
      // 对响应错误做点什么
      return Promise.reject(error);
    },
  );

  return http;
};

export class VAxios {
  constructor() { }

  /**
   * get 请求
   * @param config
   * @param options
   */
  get<T = any>(
    config: AxiosRequestConfig,
    options?: RequestOptions,
  ): Promise<T> {
    return this.request({ ...config, method: 'GET' }, options);
  }

  /**
   * post 请求
   * @param config
   * @param options
   */
  post<T = any>(
    config: AxiosRequestConfig,
    options?: RequestOptions,
  ): Promise<T> {
    if (config.data) {
      for (let key in config.data) {
        if (typeof config.params[key] === 'bigint') {
          config.params[key] = config.params[key].toString();
        }
      }
    }
    return this.request({ ...config, method: 'POST' }, options);
  }

  /**
   * 上传文件
   * @param url
   * @param config
   * @param params
   */
  uploadFile<T = any>(
    url: string,
    params: UploadFileParams,
    config?: AxiosRequestConfig,
  ) {
    const formData = new window.FormData();
    const customFilename = params.name || 'file';
    if (!config) {
      config = {
        url: url,
      };
    } else {
      config.url = url;
    }
    if (params.filename) {
      formData.append(customFilename, params.file, params.filename);
    } else {
      formData.append(customFilename, params.file);
    }

    if (params.data) {
      Object.keys(params.data).forEach((key) => {
        const value = params.data![key];
        if (Array.isArray(value)) {
          value.forEach((item) => {
            formData.append(`${key}[]`, item);
          });
          return;
        }

        formData.append(key, params.data![key]);
      });
    }
    return this.request<T>(
      {
        ...config,
        method: 'POST',
        data: formData,
        headers: Object.assign(params.headers ?? {}, {
          'Content-type': ContentTypeEnum.FORM_DATA,
          // @ts-ignore
          'ignoreCancelToken': true,


        }),
      },
      {
        Timeout: 1000 * 60,
      },
    );
  }

  /**
   * 生成签名
   * @param config
   * @param userToken
   */
  // createSign(config: AxiosRequestConfig, userToken: string) {
  //   if (!config.headers) {
  //     config.headers = {};
  //   }
  //   const requestId = uuid();
  //   const requesttime = Math.floor(new Date().getTime());
  //   const appKey = 'alliance-login:1000';
  //   const appSecret = 'ec8ec25163214cddbda35fba831cad2a';
  //   //读取query参数,按照首字符串字母大小写排序
  //   let urlParasData = '';
  //   let postMd5 = '';
  //   if (config.params) {
  //     let keys = Object.keys(config.params).sort();
  //     urlParasData = join(
  //       keys.map((key) => key + '=' + config.params[key]),
  //       '|',
  //     );
  //   }
  //   if (config.method == 'POST' && config.data) {
  //     config.data = JSONBIGINT.stringify(config.data);

  //     config.headers['Content-Type'] = 'application/json';
  //     postMd5 = CryptoJS.MD5(config.data).toString();
  //   }
  //   var vData = `${requestId}${requesttime}${appKey}${postMd5}${userToken}${urlParasData}${appSecret}`;
  //   var sign = CryptoJS.MD5(vData).toString();
  //   config.headers['requestId'] = requestId;
  //   config.headers['requesttime'] = requesttime.toString();
  //   config.headers['appkey'] = appKey;
  //   config.headers['sign'] = sign;
  //   config.headers['sver'] = '1'; //版本号
  // }
  /**
   * 底层请求
   * @param config
   * @param options
   */
  request<T = any>(
    config: AxiosRequestConfig,
    options?: RequestOptions,
  ): Promise<T> {
    return new Promise((resolve, reject) => {
      const userToken = Cookie.getCookie('Token');
      if (userToken && userToken != '') {
        config.headers = Object.assign(
          {
            Authorization: userToken,
          },
          config.headers,
        );
      }
      if (!config.headers) {
        config.headers = {};
      }
      //生成签名
      // this.createSign(config, userToken??"");

      const opt: RequestOptions = Object.assign({}, requestOptions, options);
      let axiosInstance = createAxios(opt);
      axiosInstance
        .request<any, AxiosResponse<Result>>(config)
        .then((res: AxiosResponse<Result>) => {
          const { data } = res;
          //  这里 code，result，message为 后台统一的字段，需要在 types.ts内修改为项目自己的接口返回格式
          const { Code, Result, Message } = data;
          if (Code == 401) {
            console.log(data);
            //无授权,调到登陆页面
            Local.set('NoAuthorization', '2');
            //防抖,放在在同一时间出现太多窗口
            loginInvalidDebounce();
            reject(new Error(Message));
            return;
          }
          // 这里逻辑可以根据项目进行修改
          const hasSuccess =
            data && Reflect.has(data, 'Code') && Code === ResultEnum.SUCCESS;
          if (hasSuccess) {
            resolve(Result as unknown as Promise<T>);
            return;
          }
          if (opt?.ErrorMessageMode === 'modal') {
            ElNotification.error({
              title: '提示',
              message: Message,
            });
          } else if (opt?.ErrorMessageMode === 'message') {
            ElNotification.error({
              title: '错误',
              message: Message,
            });
          } else if (opt?.ErrorMessageMode === 'confirm') {
            const errMsg = Message.replace(/\r\n/g, '<br/>').replace(
              /\n/g,
              '<br/>',
            );
            ElMessage.error({
              message: errMsg,
            });
          }
          reject(new Error(Message));
          //resolve(res as unknown as Promise<T>);
        })
        .catch((e: Error | AxiosError) => {
          if (axios.isAxiosError(e)) {
            // rewrite error message from axios in here
          }
          const { response, code, message } = (e as any) || {};
          const msg: string =
            response?.data?.error?.message ?? '服务器占时无法访问';
          const err: string = e?.toString?.() ?? '';
          let errMessage = msg;
          if (code === 401) {
            console.log('掉线。。');
          }
          if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
            errMessage = '接口超时，请稍后再试。';
          }
          if (err?.includes('Network Error')) {
            errMessage = '网络错误，请检查网络。';
          }
          if (opt?.ErrorMessageMode === 'modal') {
            ElNotification.error({
              title: '提示',
              message: errMessage,
            });
          } else if (opt?.ErrorMessageMode === 'message') {
            ElNotification.error({
              title: '错误',
              message: errMessage,
            });
          }
          reject(e);
        });
    });
  }
}

export const defHttp = new VAxios();
//登陆失效防抖
const loginInvalidDebounce = debounce(() => {
  Local.remove('NoAuthorization');
  if (window.location.href.includes('login')) {
    return;
  }
  ElMessageBox.confirm(
    '您的账号在其他电脑登录，当前已被迫退出登录!',
    '重新登陆',
    {
      confirmButtonText: 'OK',
      type: 'error',
    }
  )
    .then(() => {
      Local.remove('NoAuthorization');
      Cookie.removeCookie('Authorization');
      window.location.href = '#/';
      window.location.reload();
    })
}, 500);

/**
 * 返回结果
 */
export interface Result<T = any> {
  Code: number;
  Message: string;
  Result: T;
}

const timeRegExp = /\d{4}-\d{2}-\d{2}\s{1}\d{2}:\d{2}:\d{2}/;
const timeRegExp2 = /\d{4}-\d{2}-\d{2}.{1}\d{2}:\d{2}:\d{2}\.\d{2,}/;

/**
 * @description: 格式化返回时间
 */
export function formatResponseDate(params: Recordable) {
  if (Object.prototype.toString.call(params) !== '[object Object]') {
    return;
  }
  for (const key in params) {
    if (!params[key]) {
      continue;
    }
    const value = params[key];
    if (isString(value)) {
      if (value.length == 19) {
        if (timeRegExp.test(params[key])) {
          params[key] = new Date(params[key]);
        }
        continue;
      }
      if (timeRegExp2.test(params[key])) {
        params[key] = new Date(params[key]);
        continue;
      }
    }
    if (isArray(params[key])) {
      for (let i = 0; i < params[key].length; i++) {
        formatResponseDate(params[key][i]);
      }
      continue;
    }
    if (isObject(params[key])) {
      formatResponseDate(params[key]);
      continue;
    }
  }
}
/**
 * 请求数据格式化
 * @param params 数据
 * @returns
 */
export function formatRequestData(params: any) {
  if (Object.prototype.toString.call(params) !== '[object Object]') {
    return;
  }
  for (const key in params) {
    if (typeof params[key] === 'bigint') {
      params[key] = params[key].toString();
      continue;
    }
    console.log(key, params[key], typeof params[key]);
    if (!params[key]) {
      continue;
    }

    if (isObject(params[key])) {
      params[key] = params[key].toString();
      continue;
    }
  }
}
